//============================================================================
// Name        : DocumentIndex.cpp
// Author      : lucmir@dcc.ufmg.br
// Description :
//============================================================================

#include "DocumentIndex.h"


DocumentIndex::DocumentIndex() {
	this->numDocs = 0;
	this->docsLenSum = 0;
}

bool DocumentIndex::readUrls(std::string urlsFileName) {
	std::ifstream file;
	std::string url, line;

	file.open(urlsFileName.c_str());
	if (!file.is_open()) {
		std::cout << "\nERRO: Não foi possível abrir o arquivo: \""
				<< urlsFileName << "\"\n\n";
		return false;
	}
	while (!file.eof()) {
		getline(file, line);
		std::istringstream istr(line);
		istr >> url;
		// Check for duplicated URL
		if(!this->containsDoc(url)) {
			this->insertDoc(new DocumentInfo(url, 0.0, 0.0));
		}
	}
	file.close();

	return true;
}

DocumentIndex::~DocumentIndex() {
	std::map<unsigned int, DocumentInfo *>::iterator itr;
	for (itr = this->idDocMap.begin(); itr != this->idDocMap.end(); itr++) {
		delete itr->second;
	}
	this->docIdMap.clear();
	this->idDocMap.clear();
}

DocumentInfo *DocumentIndex::containsDoc(std::string doc) {
	std::map<std::string, unsigned int>::iterator itr;

	itr = this->docIdMap.find(doc);
	if ( itr == this->docIdMap.end() ) {
		return NULL;
	}
	return this->idDocMap.find(itr->second)->second;
}

unsigned int DocumentIndex::insertDoc(DocumentInfo *doc) {
	this->docIdMap[doc->getUrl()] = ++this->numDocs;
	this->idDocMap[this->numDocs] = doc;
	return this->numDocs;
}

void DocumentIndex::insertDoc(DocumentInfo *doc, unsigned int docId) {
	this->docIdMap[doc->getUrl()] = docId;
	this->idDocMap[docId] = doc;
	this->numDocs++;
}

unsigned int DocumentIndex::getNumDocs() {
	return this->numDocs;
}

unsigned long long DocumentIndex::getDocsLenSum() {
	return this->docsLenSum;
}

DocumentInfo *DocumentIndex::getDoc(unsigned int docId) {
	std::map<unsigned int, DocumentInfo *>::iterator itr;
	itr = this->idDocMap.find(docId);
	if ( itr == this->idDocMap.end() ) {
		return NULL;
	}
	return itr->second;
}

unsigned int DocumentIndex::getDoc(std::string url) {
	std::map<std::string, unsigned int>::iterator itr;
	itr = this->docIdMap.find(url);
	if ( itr == this->docIdMap.end() ) {
		return NOT_FOUND;
	}
	return itr->second;
}

void DocumentIndex::updateDocInfo(unsigned int docId, unsigned int freq,
	unsigned int numDocs, unsigned int tf) {
	DocumentInfo *docInfo = this->getDoc(docId);

	// Update document weight
	double wt = log(1 + ((double)numDocs/(double)tf) );
	double wdt = (1 + log(freq))*wt;
	docInfo->setWeight(pow(wdt, 2));

	// Update document length (number of terms in document)
	docInfo->incLen(freq);
	this->docsLenSum += freq;
}

bool DocumentIndex::printToFile(std::string documentsFileName) {
	std::ofstream file;
	std::map<unsigned int, DocumentInfo *>::iterator itr;

	// Document file
	file.open(documentsFileName.c_str());
	if(!file.is_open()) {
		std::cout << "\nERRO: Não foi possível abrir o arquivo: " << documentsFileName << "\n";
		return false;
	}
	double avgDocLen = (this->numDocs > 0) ? ((double)this->docsLenSum / (double)this->numDocs) : 1;
	for (itr = this->idDocMap.begin(); itr != this->idDocMap.end(); itr++) {
		// Print - Normalize docLen and weight
		double docLen = (itr->second->getLen()/avgDocLen);
		file << itr->first << "\t" << itr->second->getUrl() << "\t"
				<< docLen << "\t"
				<< sqrt(itr->second->getWeight()) << "\n";
	}
	file.close();

	return true;
}

bool DocumentIndex::readFromFile(std::string documentsFileName) {
	std::ifstream file;
	std::string line, url;
	double weight, len;
	unsigned int docId;

	// Document file
	file.open(documentsFileName.c_str());
	if (!file.is_open()) {
		std::cout << "\nERRO: Não foi possível abrir o arquivo: \"" << documentsFileName << "\"\n\n";
		return false;
	}
	while(!file.eof()) {
		getline(file, line);
		std::istringstream istr(line);
		istr >> docId;
		istr >> url;
		istr >> len;
		istr >> weight;
		this->insertDoc(new DocumentInfo(url, weight, len), docId);
		this->docsLenSum += len;
	}
	file.close();

	return true;
}
